/**
 * Copyright © 2014-2021 The SiteWhere Authors
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.sitewhere.device.microservice;

import com.sitewhere.device.DeviceManagementTriggers;
import com.sitewhere.device.configuration.DeviceManagementTenantConfiguration;
import com.sitewhere.device.configuration.DeviceManagementTenantEngineModule;
import com.sitewhere.device.grpc.DeviceManagementImpl;
import com.sitewhere.device.kafka.DeviceInteractionEventsProducer;
import com.sitewhere.device.spi.kafka.IDeviceInteractionEventsProducer;
import com.sitewhere.device.spi.microservice.IDeviceManagementMicroservice;
import com.sitewhere.device.spi.microservice.IDeviceManagementTenantEngine;
import com.sitewhere.grpc.service.DeviceManagementGrpc;
import com.sitewhere.microservice.api.device.DeviceManagementRequestBuilder;
import com.sitewhere.microservice.api.device.IDeviceManagement;
import com.sitewhere.microservice.datastore.DatastoreDefinition;
import com.sitewhere.microservice.lifecycle.CompositeLifecycleStep;
import com.sitewhere.microservice.scripting.Binding;
import com.sitewhere.rdb.RdbPersistenceOptions;
import com.sitewhere.rdb.RdbTenantEngine;
import com.sitewhere.rest.model.search.device.DeviceTypeSearchCriteria;
import com.sitewhere.spi.SiteWhereException;
import com.sitewhere.spi.device.IDeviceType;
import com.sitewhere.spi.microservice.IFunctionIdentifier;
import com.sitewhere.spi.microservice.MicroserviceIdentifier;
import com.sitewhere.spi.microservice.lifecycle.ICompositeLifecycleStep;
import com.sitewhere.spi.microservice.lifecycle.ILifecycleProgressMonitor;
import com.sitewhere.spi.microservice.multitenant.IMicroserviceTenantEngine;
import com.sitewhere.spi.microservice.multitenant.ITenantEngineModule;
import com.sitewhere.spi.microservice.scripting.IScriptVariables;
import com.sitewhere.spi.search.ISearchResults;

import io.sitewhere.k8s.crd.tenant.engine.SiteWhereTenantEngine;

/**
 * Implementation of {@link IMicroserviceTenantEngine} that implements device
 * management functionality.
 */
public class DeviceManagementTenantEngine extends RdbTenantEngine<DeviceManagementTenantConfiguration>
	implements IDeviceManagementTenantEngine {

    /** Device management persistence API */
    private IDeviceManagement deviceManagement;

    /** Responds to device management GRPC requests */
    private DeviceManagementGrpc.DeviceManagementImplBase deviceManagementImpl;

    /** Produces events generated by device interactions */
    private IDeviceInteractionEventsProducer deviceInteractionEventsProducer;

    public DeviceManagementTenantEngine(SiteWhereTenantEngine engine) {
	super(engine);
    }

    /*
     * @see com.sitewhere.spi.microservice.multitenant.IMicroserviceTenantEngine#
     * getConfigurationClass()
     */
    @Override
    public Class<DeviceManagementTenantConfiguration> getConfigurationClass() {
	return DeviceManagementTenantConfiguration.class;
    }

    /*
     * @see com.sitewhere.spi.microservice.multitenant.IMicroserviceTenantEngine#
     * createConfigurationModule()
     */
    @Override
    public ITenantEngineModule<DeviceManagementTenantConfiguration> createConfigurationModule() {
	return new DeviceManagementTenantEngineModule(this, getActiveConfiguration());
    }

    /*
     * @see com.sitewhere.rdb.spi.IRdbTenantEngine#getDatastoreDefinition()
     */
    @Override
    public DatastoreDefinition getDatastoreDefinition() {
	return getActiveConfiguration().getDatastore();
    }

    /*
     * @see com.sitewhere.rdb.spi.IRdbTenantEngine#getEntitiesBasePackage()
     */
    @Override
    public String getEntitiesBasePackage() {
	return "com.sitewhere.device.persistence.rdb.entity";
    }

    /*
     * @see com.sitewhere.microservice.multitenant.MicroserviceTenantEngine#
     * loadEngineComponents()
     */
    @Override
    public void loadEngineComponents() throws SiteWhereException {
	// // Create management interfaces.
	IDeviceManagement implementation = getInjector().getInstance(IDeviceManagement.class);
	this.deviceManagement = new DeviceManagementTriggers(implementation, this);
	this.deviceManagementImpl = new DeviceManagementImpl((IDeviceManagementMicroservice) getMicroservice(),
		getDeviceManagement());
    }

    /*
     * @see com.sitewhere.rdb.RdbTenantEngine#getPersistenceOptions()
     */
    @Override
    public RdbPersistenceOptions getPersistenceOptions() {
	RdbPersistenceOptions options = new RdbPersistenceOptions();
	// options.setHbmToDdlAuto("update");
	return options;
    }

    /*
     * @see com.sitewhere.microservice.multitenant.MicroserviceTenantEngine#
     * hasExistingDataset()
     */
    @Override
    public boolean hasExistingDataset() throws SiteWhereException {
	DeviceTypeSearchCriteria criteria = new DeviceTypeSearchCriteria(1, 1);
	ISearchResults<? extends IDeviceType> results = getDeviceManagement().listDeviceTypes(criteria);
	return results.getNumResults() > 0 ? true : false;
    }

    /*
     * @see com.sitewhere.microservice.multitenant.MicroserviceTenantEngine#
     * setDatasetBootstrapBindings(com.sitewhere.microservice.scripting.Binding)
     */
    @Override
    public void setDatasetBootstrapBindings(Binding binding) throws SiteWhereException {
	binding.setVariable(IScriptVariables.VAR_DEVICE_MANAGEMENT_BUILDER,
		new DeviceManagementRequestBuilder(getDeviceManagement()));
    }

    /*
     * @see com.sitewhere.microservice.multitenant.MicroserviceTenantEngine#
     * getTenantBootstrapPrerequisites()
     */
    @Override
    public IFunctionIdentifier[] getTenantBootstrapPrerequisites() {
	return new IFunctionIdentifier[] { MicroserviceIdentifier.AssetManagement };
    }

    /*
     * @see com.sitewhere.spi.microservice.multitenant.IMicroserviceTenantEngine#
     * tenantInitialize(com.sitewhere.spi.microservice.lifecycle.
     * ILifecycleProgressMonitor)
     */
    @Override
    public void tenantInitialize(ILifecycleProgressMonitor monitor) throws SiteWhereException {
	super.tenantInitialize(monitor);

	// Device interaction events producer.
	this.deviceInteractionEventsProducer = new DeviceInteractionEventsProducer();

	// Create step that will initialize components.
	ICompositeLifecycleStep init = new CompositeLifecycleStep("Initialize " + getComponentName());

	// Initialize device management persistence.
	init.addInitializeStep(this, getDeviceManagement(), true);

	// Initialize device interaction events producer.
	init.addInitializeStep(this, getDeviceInteractionEventsProducer(), true);

	// Execute initialization steps.
	init.execute(monitor);
    }

    /*
     * @see com.sitewhere.spi.microservice.multitenant.IMicroserviceTenantEngine#
     * tenantStart(com.sitewhere.spi.microservice.lifecycle.
     * ILifecycleProgressMonitor)
     */
    @Override
    public void tenantStart(ILifecycleProgressMonitor monitor) throws SiteWhereException {
	super.tenantStart(monitor);

	// Create step that will start components.
	ICompositeLifecycleStep start = new CompositeLifecycleStep("Start " + getComponentName());

	// Start device management persistence.
	start.addStartStep(this, getDeviceManagement(), true);

	// Start device interaction events producer.
	start.addStartStep(this, getDeviceInteractionEventsProducer(), true);

	// Execute startup steps.
	start.execute(monitor);
    }

    /*
     * @see com.sitewhere.spi.microservice.multitenant.IMicroserviceTenantEngine#
     * tenantStop(com.sitewhere.spi.microservice.lifecycle.
     * ILifecycleProgressMonitor)
     */
    @Override
    public void tenantStop(ILifecycleProgressMonitor monitor) throws SiteWhereException {
	// Create step that will stop components.
	ICompositeLifecycleStep stop = new CompositeLifecycleStep("Stop " + getComponentName());

	// Stop device interaction events producer.
	stop.addStopStep(this, getDeviceInteractionEventsProducer());

	// Stop device management persistence.
	stop.addStopStep(this, getDeviceManagement());

	// Execute shutdown steps.
	stop.execute(monitor);

	super.tenantStop(monitor);
    }

    /*
     * (non-Javadoc)
     * 
     * @see com.sitewhere.device.spi.microservice.IDeviceManagementTenantEngine#
     * getDeviceManagement()
     */
    @Override
    public IDeviceManagement getDeviceManagement() {
	return deviceManagement;
    }

    /*
     * (non-Javadoc)
     * 
     * @see com.sitewhere.device.spi.microservice.IDeviceManagementTenantEngine#
     * getDeviceManagementImpl()
     */
    @Override
    public DeviceManagementGrpc.DeviceManagementImplBase getDeviceManagementImpl() {
	return deviceManagementImpl;
    }

    /*
     * @see com.sitewhere.device.spi.microservice.IDeviceManagementTenantEngine#
     * getDeviceInteractionEventsProducer()
     */
    @Override
    public IDeviceInteractionEventsProducer getDeviceInteractionEventsProducer() {
	return deviceInteractionEventsProducer;
    }
}